home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Graphics Plus
/
Graphics Plus.iso
/
msdos
/
viewers
/
hv12
/
src
/
dither.c
next >
Wrap
C/C++ Source or Header
|
1992-07-17
|
7KB
|
245 lines
/*
NAME
dither - routines to do Floyd-Steinberg dithering
DESCRIPTION
The assumption is that you have input values in the range
minIn..maxIn that you want to reduce to the range
minOut..maxOut. (For color images, you would dither each color
component separately.)
Dither a serpentine version of Floyd-Steinberg dithering. The
skeleton code to use these routines is:
DitherInit();
onOddRow = FALSE;
for (each row) {
Dither(row, onOddRow);
onOddRow = !onOddRow;
}
DitherTerm();
*/
#include "jinclude.h"
#include "jmemsys.h"
#include "viewer.h"
#include "hicolor.h"
#include "dither.h"
#define DInflateBase 15
extern BYTE ColorLUT256[];
/*
NAME
Dither - apply Floyd-Steinberg dithering to one row
DESCRIPTION
Adapted from the JPEG source code.
Note that it is acceptable to have pIn == pOut, i.e., to do the
dithering in place.
*/
void
Dither (
DCtrlType * pCtrl, /* structure with control parameters */
int *pIn, /* input values */
int *pOut, /* output values */
int onOddRow /* TRUE => current row is odd-numbered */
)
{
int col; /* column counter */
int dir; /* direction flag */
int error; /* value of current error */
int *nextRowErr; /* pointer to error buffer for next row */
int *thisRowErr; /* pointer to error buffer for this row */
ulong scale = pCtrl->scale;
ulong invscale = pCtrl->invscale;
int val; /* holds dithered value */
int width = pCtrl->numCols;
if (onOddRow)
{ /* work right to left in this row */
pIn += width - 1;
pOut += width - 1;
dir = -1;
thisRowErr = pCtrl->oddRowErrs + 1;
nextRowErr = pCtrl->evenRowErrs + width;
}
else
{ /* work left to right in this row */
dir = 1;
thisRowErr = pCtrl->evenRowErrs + 1;
nextRowErr = pCtrl->oddRowErrs + width;
}
nextRowErr[0] = 0; /* need only initialize this one entry */
for (col = width; col > 0; col--)
{
/* get current value and adjust for accumulated error */
val = ((*pIn) << 4) + thisRowErr[0]; /* errors are in units of 1/16 */
if (val < 0)
val = 0;
else
{
#ifdef OLD_CODE
val += 8; /* for rounding */
#endif
val >>= 4;
if (val > pCtrl->maxIn)
val = pCtrl->maxIn;
}
/* reduce input value to dithered value */
*pOut = (int) (pCtrl->minOut +
((scale * (val - pCtrl->minIn) + (1L << (DInflateBase - 1))) >> DInflateBase));
/* calculate error (in units of 1/16 value) in dithered value */
error = -(int) (pCtrl->minIn + (((*pOut - pCtrl->minOut) * invscale
+ (1L << (DInflateBase - 1))) >> DInflateBase) - val);
/* store the error */
val = 2 * error;
nextRowErr[-1] = error; /* not +=, since not initialized yet */
error += val; /* form error * 3 */
nextRowErr[1] += error;
error += val; /* form error * 5 */
nextRowErr[0] += error;
error += val; /* form error * 7 */
thisRowErr[1] += error;
pIn += dir;
pOut += dir;
thisRowErr += 1;
nextRowErr -= 1;
}
}
void
DitherChar (
DCtrlType * pCtrl, /* structure with control parameters */
JSAMPLE *pIn, /* input values */
JSAMPLE *pOut, /* output values */
int onOddRow /* TRUE => current row is odd-numbered */
)
{
int col; /* column counter */
int dir; /* direction flag */
int error; /* value of current error */
int *nextRowErr; /* pointer to error buffer for next row */
int *thisRowErr; /* pointer to error buffer for this row */
ulong scale = pCtrl->scale;
ulong invscale = pCtrl->invscale;
int val; /* holds dithered value */
int width = pCtrl->numCols;
if (onOddRow)
{ /* work right to left in this row */
pIn += width - 1;
pOut += width - 1;
dir = -1;
thisRowErr = pCtrl->oddRowErrs + 1;
nextRowErr = pCtrl->evenRowErrs + width;
}
else
{ /* work left to right in this row */
dir = 1;
thisRowErr = pCtrl->evenRowErrs + 1;
nextRowErr = pCtrl->oddRowErrs + width;
}
nextRowErr[0] = 0; /* need only initialize this one entry */
for (col = width; col > 0; col--)
{
/* get current value and adjust for accumulated error */
val = ((ColorLUT256[GETJSAMPLE(*pIn)]) << 4) + thisRowErr[0]; /* errors are in units of 1/16 */
if (val < 0)
val = 0;
else
{
#ifdef OLD_CODE
val += 8; /* for rounding */
#endif
val >>= 4;
if (val > pCtrl->maxIn)
val = pCtrl->maxIn;
}
/* reduce input value to dithered value */
*pOut = (JSAMPLE) (pCtrl->minOut +
((scale * (val - pCtrl->minIn) + (1L << (DInflateBase - 1))) >> DInflateBase));
/* calculate error (in units of 1/16 value) in dithered value */
error = -(int) (pCtrl->minIn + (((*pOut - pCtrl->minOut) * invscale
+ (1L << (DInflateBase - 1))) >> DInflateBase) - val);
/* store the error */
val = 2 * error;
nextRowErr[-1] = error; /* not +=, since not initialized yet */
error += val; /* form error * 3 */
nextRowErr[1] += error;
error += val; /* form error * 5 */
nextRowErr[0] += error;
error += val; /* form error * 7 */
thisRowErr[1] += error;
pIn += dir;
pOut += dir;
thisRowErr += 1;
nextRowErr -= 1;
}
}
/*
NAME
DitherInit - initialize dither buffers and control parameters
RETURN VALUES
0 if all OK
1 on error (probably insufficient memory)
*/
int
DitherInit (
BYTE minIn, /* minimum input value */
BYTE maxIn, /* maximum input value */
BYTE minOut, /* minimum output value */
BYTE maxOut, /* maximum output value */
int numCols, /* number of columns in each row */
DCtrlType * pCtrl /* structure with control parameters */
)
{
pCtrl->minIn = minIn;
pCtrl->maxIn = maxIn;
pCtrl->minOut = minOut;
pCtrl->maxOut = maxOut;
pCtrl->numCols = numCols;
pCtrl->scale = ((maxOut - minOut) << DInflateBase) / (maxIn - minIn);
pCtrl->invscale = ((maxIn - minIn) << DInflateBase) / (maxOut - minOut);
pCtrl->oddRowErrs = (int *) jget_small ((numCols + 2) * sizeof (int));
if (pCtrl->oddRowErrs == NULL)
return 1;
memset (pCtrl->oddRowErrs, 0, (numCols + 2) * sizeof (int));
pCtrl->evenRowErrs = (int *) jget_small ((numCols + 2) * sizeof (int));
if (pCtrl->evenRowErrs == NULL)
return 1;
memset (pCtrl->evenRowErrs, 0, (numCols + 2) * sizeof (int));
return 0;
}
/*
NAME
DitherTerm - clean up after dither operation
DESCRIPTION
Frees up memory used for error buffers.
*/
void
DitherTerm (
DCtrlType * pCtrl /* structure with control parameters */
)
{
jfree_small (pCtrl->oddRowErrs);
jfree_small (pCtrl->evenRowErrs);
}